{
    class GameController extends Laya.EventDispatcher {
        constructor() {
            super();
            this.roomNo = '';
            this.controlView = null;
            this.roles = {};
            this.bullets = {};
            this.self = null;

            //前端测试数据
            this._bulletNum = 0;
            this._bulletId = 0;
            this.lastWalkCmd = '';

            this.actions = {
                "control.roker.start": this.lockTarget,
                "control.roker.do": this.attackOrSkill,
                "sync": this.gameRender,
                "start": this.start,
                "end": this.end,
                "kill": this.showKill,
                "role.fire"(data) {
                    this.roles[data.uid].attack(data.a);
                    this.addShellCase(this.roles[data.uid]);
                }
                // "control.move": this.roleMove
            };
            Sail.io.register(this.actions, this);
        }

        destoryEvent() {
            Sail.io.unregister(this.actions, this);

            for (var uid in this.roles) {
                let role = this.roles[uid];
                role.destoryEvent();
            }
        }

        init(roomNo, controlView) {
            this.roomNo = roomNo;
            this.controlView = controlView;
        }

        start(data) {
            if (data == 0) {
                this.controlView.active = true;
                Laya.timer.loop(50, this, this.roleMove);
            }
            this.controlView && this.controlView.start(data);
        }

        end(data) {
            Laya.timer.clearAll(this);
            this.event('game_end', [data]);

            Controller.Bullet.I.reset();
        }

        //创建人物
        createRole(data, camp) {
            data.camp = camp;
            var player = new Com.Room.Role();
            player.pos(data.x, data.y);
            player.zOrder = data.y;
            player.create(data);
            player.hero.setFace(data.mx > 0 ? 1 : -1);
            Sail.io.publish('map.add', player);
            this.roles[data.uid] = player;
            if (data.uid == GM.user_id) {
                this.self = player;
                this.self.on('death', this.controlView, this.controlView.showTime);
                this.controlView.init(player.heroData);
            }
        }

        // todo本地先移动
        roleMove() {
            if (GM.debug) {
                this.move();
                return;
            }
            let direct = this.controlView.moveRokerControl.direct;
            let speedN = this.controlView.moveRokerControl.speed > 0.5 ? 1 : this.controlView.moveRokerControl.speed * 2;
            let cmd = ''
            if (speedN == 0) {
                cmd = 'stop';
                if (this.lastWalkCmd != cmd) {
                    Sail.io.emit('stop', { roomNo: this.roomNo });
                    this.lastWalkCmd = cmd;
                }
            } else {
                if (isNaN(direct[0]) || isNaN(direct[1])) {
                    return;
                }
                cmd = 'go::speed:' + speedN + ',x:' + direct[0] + ',y:' + direct[1];
                if (this.lastWalkCmd != cmd) {
                    Sail.io.emit('go', { roomNo: this.roomNo, x: direct[0], y: direct[1], d: speedN * 100 });
                    this.lastWalkCmd = cmd;
                }
            }
        }

        lockTarget(type) {
            let p = new Laya.Point(this.self.x, this.self.y);
            let max = 10000;
            let roker;
            let range;
            if (type == 'attack') {
                roker = this.controlView.aimRokerControl;
                range = this.self.heroData.normal.indicator.range;
            } else {
                roker = this.controlView.skillRokerControl;
                range = max;
            }
            //找到距离自己最近的并且在射程范围内的目标
            let target = null;
            for (var uid in this.roles) {
                let role = this.roles[uid];
                //排除自己
                if (uid == GM.user_id) {
                    continue;
                }
                //排除隐身不可见的角色
                if (role.status == ROLE_STATUS.HIDING && role.isVisual == false) {
                    continue;
                }
                let dis = p.distance(role.x, role.y);
                if (dis < max && dis < range) {
                    max = dis;
                    target = role;
                }
            }
            //如果目标存在自动锁定
            if (target) {
                roker.speed = 1;
                roker.direct = [target.x - this.self.x, target.y - this.self.y];
                roker.angle = Math.atan2(roker.direct[1], roker.direct[0]) * 180 / Math.PI;
            }
            //显示指示器
            Laya.timer.frameLoop(1, this, this.showIndicator);
        }

        attackOrSkill(type) {
            Laya.timer.clear(this, this.showIndicator);
            this.controlView.hideIndicator();
            // console.trace(type);

            let target = (type == 'attack') ? this.controlView.aimRokerControl : this.controlView.skillRokerControl;
            if (target.speed != 0) {
                if (GM.debug) {
                    let d = type == 'attack' ? this.self.heroData.normal : this.self.heroData.skill;

                    this.doFire(d, target.direct);
                    if (type == 'attack') {
                        Sail.io.publish('skill.add.power', 0.2);
                    } else {
                        Sail.io.publish('skill.set.power', 0);
                    }
                    return;
                }
                switch (type) {
                    case "attack":
                        Sail.io.emit("fire", { a: target.angle < 0 ? 360 + target.angle : target.angle, roomNo: this.roomNo });
                        break;
                    case "skill":
                        Sail.io.emit("clutchshot", { a: target.angle < 0 ? 360 + target.angle : target.angle, roomNo: this.roomNo });
                        break;
                }
            } else {
                this.self.hideIndicator();
            }
        }

        showIndicator() {
            if (this.self.isAttack) {
                return;
            }
            let roker = this.controlView.aimRokerControl.active ? this.controlView.aimRokerControl : this.controlView.skillRokerControl;

            let speed = roker.speed;
            if (speed > 0) {
                let direct = roker.direct;

                Sail.io.publish('role.change.face', direct);
                //根据自己人物武器得到的指示器位置
                let p = this.self.localToGlobal(new Laya.Point(40, 120));
                let isNoBullet = this.self.bulletNum <= 0;
                this.controlView.showIndicator(p, isNoBullet);
            }
        }


        doFire(data, direct) {
            this.self.attack();
            //前端模拟子弹发射
            this._bulletNum = 0;
            Laya.timer.frameLoop(6, this, this.addBullet, [data, direct]);
        }

        addBullet(data, direct) {
            let angle = Math.atan2(direct[1], direct[0]) * 180 / Math.PI;
            let d = data.indicator.range;
            if (data.type == 1) {
                this._bulletNum += 1;
                let b = this.createBullet(data.asset, angle);
                b.pos(this.self.x, this.self.y - 32);
                Sail.io.publish('map.add', b);
                let dis = Math.sqrt(Math.pow(direct[0], 2) + Math.pow(direct[1], 2));
                let targetX = b.x + d / dis * direct[0];
                let targetY = b.y + d / dis * direct[1];
                Laya.Tween.to(b, { x: targetX, y: targetY }, d / data.speed * 1000, Laya.Ease.linearIn, Laya.Handler.create(this, () => { b.removeSelf(); delete this.bullets[b.bid]; }));
            } else {
                this._bulletNum += data.bulletNum;
                let a = data.indicator.width / (data.bulletNum + 1);
                for (let i = 0; i < data.bulletNum; i++) {
                    let ea = angle - data.indicator.width / 2 + (i + 1) * a;
                    let b = this.createBullet(data.asset, ea);
                    b.pos(this.self.x, this.self.y - 65);
                    Sail.io.publish('map.add', b);
                    let h = ea * Math.PI / 180;
                    let targetX = b.x + Math.cos(h) * d;
                    let targetY = b.y + Math.sin(h) * d;
                    Laya.Tween.to(b, { x: targetX, y: targetY }, d / data.speed * 1000, Laya.Ease.linearIn, Laya.Handler.create(this, () => { b.removeSelf(); delete this.bullets[b.bid]; }));
                }
            }
            this.addShellCase(this.self);
            if (this._bulletNum == data.bulletNum) {
                Laya.timer.clear(this, this.addBullet);
            }
        }


        createBullet(asset, angle) {
            this._bulletId += 1;
            let arr = asset.split(':');
            let assetType = arr[1];
            let b;
            if (assetType == '1') {
                b = new Laya.Image('res/game/bullet/' + arr[0] + '.png');
                b.pivotX = -130;
            } else {
                b = new Laya.Animation();
                b.loadAnimation('Room/Ani/' + arr[0] + '.ani');
                b.play();
            }
            b.anchorY = 0.5;
            b.rotation = angle;
            b.bid = this._bulletId;
            b.zOrder = 15 * 144;
            this.bullets[b.bid] = b;
            return b;
        }

        /**
         * 添加弹壳
         * @param {*} role 攻击者
         */
        addShellCase(role) {
            let shellAni = new Laya.Animation();
            shellAni.loadAnimation('Room/Ani/ShellCase.ani');
            shellAni.pivotX = 160;
            shellAni.zOrder = role.zOrder;
            shellAni.scaleX = role.getFace();
            shellAni.pos(role.x, role.y - 80);
            shellAni.play(0, false);
            shellAni.on('complete', this, () => { shellAni.removeSelf() });
            Sail.io.publish('map.add', shellAni);
        }

        //大循环回调
        //{"cmd":"sync","code":0,"res":{"role":[{"x":4968,"y":2160,"d":0,"mx":-1,"my":0,"uid":2037586642},{"x":72,"y":792,"d":0,"mx":1,"my":0,"uid":2037587422}]}}
        gameRender(data, code) {
            if (code != 0) {
                return;
            }
            //人物行走渲染
            let r = {};
            // let bulletsBurn = data.bb;
            // let bulletsDestroy = data.bd;
            let itemData = data.cell;

            data.role.forEach(function (element) {
                element.isVisual = (data.visual.indexOf(element.uid) > -1);
                r[element.uid] = element;
            }, this);

            for (let uid in this.roles) {
                let role = this.roles[uid];
                let d = r[role.uid];
                if (d) {
                    if (d.status != -1) {
                        let roleData = {
                            hp: d.hp, bulletNum: d.clip.cnt, positionX: d.x, positionY: d.y, speedN: d.d / 100, direct: [d.mx, d.my],
                            status: d.status, isVisual: d.isVisual
                        };
                        role.active(roleData);
                    }
                    if (GM.debug) {
                        continue;
                    }
                    if (uid == GM.user_id && d.skill) {
                        let skill = d.skill;
                        Sail.io.publish('skill.set.power', skill.nc / skill.nt);
                    }
                } else {
                    // role.visible = false;
                }
            }

            if (GM.debug) {
                return;
            }

            Controller.Bullet.I.addBullets(data.bb).removeBullets(data.bd);

            // for (let b of bulletsBurn) {
            //     // Sail.io.publish("role.fire", b);
            //     // TODO: b.isSkill
            //     this.roles[b.uid].attack(b.a);
            //     Sail.io.publish("map.add.bullet", Com.Room.Bullet.Base.create(b));
            // }
            // for (let bid of bulletsDestroy) {
            //     Sail.io.publish("map.remove.bullet", bid);
            // }
            for (let c of itemData) {
                // Sail.io.publish("map.remove.bullet", bid);
                switch (c.s) {
                    case 1://普通子弹击中
                        Sail.io.publish("map.action", { id: c.id, act: "shake", a: c.a });
                        break;
                    case 2://终结技击毁元件
                        Sail.io.publish("map.action", { id: c.id, act: "destroy" });
                        break;
                    case 3://元件复原
                        Sail.io.publish("map.action", { id: c.id, act: "restore" });
                        break;
                }
            }
        }

        showKill(data) {
            data.camp == data.kid.camp ? Sail.io.publish('kill.blue.show', data) : Sail.io.publish('kill.red.show', data);
        }

        //前端模拟移动
        move() {
            let d;
            let player = this.roles[GM.user_id];
            let direct = this.controlView.moveRokerControl.direct;
            let speedN = this.controlView.moveRokerControl.speed > 0.5 ? 1 : this.controlView.moveRokerControl.speed * 2;
            if (isNaN(direct[0]) || isNaN(direct[1])) {
                return;
            }
            if (this.controlView.moveRokerControl.speed == 0) {
                d = { "role": [{ uid: GM.user_id, x: player.x, y: player.y, mx: 0, my: 0, d: 0, "clip": { "max": 3, "t": 5000, "p": { "x": -20, "y": -34, "x1": 84, "y1": -56 }, "s": 2000, "n": 5, "b": 200, "cnt": 3, "lt": 0 }, "hp": 1000, "status": 1, "skill": { "nc": 0, "nt": 5 }, "hit": [], "recover": [] }], "visual": [] };
            } else {
                let x = direct[0];
                let y = direct[1];
                let n = speedN;
                let dis = Math.sqrt(x * x + y * y);
                let targetX = player.x + (player.speed / dis) * x * n;
                let targetY = player.y + (player.speed / dis) * y * n;
                if (isNaN(targetX) || isNaN(targetY)) {
                    return;
                }
                targetX = Math.min(Math.max(90, targetX), 35 * 144 - 90);
                targetY = Math.min(Math.max(0, targetY), 15 * 144);
                d = { "role": [{ uid: GM.user_id, x: targetX, y: targetY, mx: direct[0], my: direct[1], d: speedN * 100, "clip": { "max": 3, "t": 5000, "p": { "x": -20, "y": -34, "x1": 84, "y1": -56 }, "s": 2000, "n": 5, "b": 200, "cnt": 3, "lt": 0 }, "hp": 1000, "status": 1, "skill": { "nc": 0, "nt": 5 }, "hit": [], "recover": [] }], "visual": [] };
                //{"x":4980,"y":1046.62,"d":0,"mx":1,"my":0,"uid":2037586642,"clip":{"max":3,"t":5000,"p":{"x":-20,"y":-34,"x1":84,"y1":-56},"s":2000,"n":5,"b":200,"cnt":3,"lt":0},"hp":1000,"status":-1,"skill":{"nc":0,"nt":5},"hit":[],"recover":[]}
            }
            Sail.io.publish('sync', d, 0);
        }
    }

    Sail.class(GameController, "Ctrl.Room.Game");
}